#!/bin/bash

#=======================================================================
# git-wip
# File ID: fe636b92-445c-11e4-940c-c80aa9e67bbd
#
# Create subbranches using '.' as level delimiter.
#
# Author: Øyvind A. Holm <sunny@sunbase.org>
# License: GNU General Public License version 2 or later.
#=======================================================================

progname=git-wip
test -n "$GITWIP_GIT" && CMD_GIT="$GITWIP_GIT" || CMD_GIT=git
curr="$($CMD_GIT rev-parse --abbrev-ref HEAD)"
use_new_branch=0
test "$curr" = "master" -o "$curr" = "HEAD" && use_new_branch=1

ext="wip"
test -n "$1" && { ext="$1"; shift; }

if test "$ext" = "-h" -o "$ext" = "--help"; then
    cat <<END

Usage:

  $progname OPTION
  $progname BRANCHNAME

Options:

  -d
    Delete current branch after merging it to parent. Used when there's 
    no need to document the existence of the branch, and a fast-forward 
    merge will do just fine.
  -h, --help
    Show this help.
  -m
    Merge the current branch to the parent branch using --no-ff in 
    git(1). This creates a merge commit even if a fast-forward is 
    possible. Useful to group related changes together.
  -p
   Change the active branch (git checkout) to the parent branch, i.e., 
   the part of the branch name before the final full stop. If no parent 
   branch exists, you will be prompted to create one on 'master'.
  -s
    Squash the branch into the parent branch, all commits will be 
    collapsed into one.

When the -d, -m, -p or -s option is used and there are no parent 
branches (there are no full stops ('.') in the current branch name), the 
branch will be merged or squashed to 'master'.

If no options are specified, a new "subbranch" will be created from the 
current branch by postfixing it with ".BRANCHNAME" where BRANCHNAME is 
specified on the command line. An exception is made when the current 
branch is 'master', in that case the old branch name is not used as 
prefix.

The GITWIP_GIT environment variable can be set to a specific git(1) 
executable to use. Example:

  export GITWIP_GIT=/usr/bin/git
  git wip -m

END
    exit 0
fi

loop_until_y() {
    # Loop until the user says 'y'.
    unset resp
    until test "$resp" = "y"; do
        echo -n $progname: Type \'y\' + Enter to $*... >&2
        read resp
    done
}

if test -n "$(echo x$ext | grep -E '^x-[dmps]$')"; then
    if test -n "$(echo "$curr" | grep '\.')"; then
        # Current branch contains a full stop, so it has a parent
        parent="$(echo "$curr" | rev | cut -d . -f 2- | rev)"
    else
        # Current branch has no parents, prepare for merge or squash to 
        # master.
        test "$ext" = "-s" && ms_str=squash || ms_str=merge
        if test "$curr" = "master"; then
            echo Is already on master, nowhere to $ms_str branch >&2
            exit 1
        fi
        if test "$ext" = "-p"; then
            loop_until_y set active branch to \'master\' \(git checkout\)
        else
            loop_until_y $ms_str $curr to master
        fi
        parent=master
    fi

    $CMD_GIT branch | cut -c 3- | grep "^$parent\$" || {
        echo $progname: $parent: Parent branch does not exist >&2
        loop_until_y create the \'$parent\' branch on \'master\'
        $CMD_GIT branch "$parent" master
    }
    $CMD_GIT checkout $parent &&
    if test "$ext" = "-d"; then
        # Delete current branch after merge to parent, use fast-forward 
        # if possible.
        $CMD_GIT merge $curr
        $CMD_GIT branch -d $curr
    elif test "$ext" = "-s"; then
        # Squash the current branch to parent, combine all commits into 
        # one.
        $CMD_GIT merge --squash $curr
    elif test "$ext" = "-m"; then
        # Merge current branch into parent, always create merge commit 
        # even if a fast-forward merge is possible.
        $CMD_GIT merge --no-ff $curr
        $CMD_GIT branch -d $curr
    fi
    exit
fi

if test -n "$(echo x$ext | grep '^x-')"; then
    echo $progname: $ext: Unknown option >&2
    exit 1
fi

if test "$use_new_branch" = "1"; then
    # Create new branch without prefix
    $CMD_GIT branch "$ext"
    $CMD_GIT checkout "$ext"
else
    # Create new branch, use current branch name as prefix
    $CMD_GIT branch "$curr.$ext"
    $CMD_GIT checkout "$curr.$ext"
fi
